#ifndef _SERVER_
#define _SERVER_

#include"util.hpp"
#include"user_table.hpp"
#include"session.hpp"
#include"online_user.hpp"
#include"room.hpp"
#include"matcher.hpp"

#define WWWROOT "./wwwroot"
#define _404_ "./wwwroot/404.html"

class Gobang_Server
{
public:
    Gobang_Server(const std::string &host,
                  const std::string &user,
                  const std::string &pass,
                  const std::string &dbname,
                  const uint16_t port=3306,
                  const std::string &wwwroot=WWWROOT)
        :_web_root(wwwroot),_ut(host,user,pass,dbname,port),_sm(&_wssrv),_rm(&_ut,&_om,&_wssrv),_mm(&_rm,&_ut,&_om)
    {
        // 设置日志级别,none表示什么都不打印
        _wssrv.set_access_channels(websocketpp::log::alevel::none);
        // 初始化asio
        _wssrv.init_asio();
        // 设置是否启⽤地址重⽤
        _wssrv.set_reuse_addr(true);
        // 注册http请求的处理函数
        _wssrv.set_http_handler(std::bind(&Gobang_Server::http_callback,this,std::placeholders::_1));
        // 注册websocket请求的处理函数
        _wssrv.set_open_handler(std::bind(&Gobang_Server::wsopen_callback,this,std::placeholders::_1));
        _wssrv.set_close_handler(std::bind(&Gobang_Server::wsclose_callback,this,std::placeholders::_1));
        _wssrv.set_message_handler(std::bind(&Gobang_Server::wsmessage_callback,this,std::placeholders::_1,std::placeholders::_2));

    }

    void start(int port)
    {
        _wssrv.listen(port);
        _wssrv.start_accept();
        _wssrv.run();
    }

    // ~Gobang_Server()
    // {

    // }
private:
    // 访问静态网页时的回调函数
    void file_handler(wsserver_t::connection_ptr &conn)
    {
        // 获取请求的uri
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();

        // 拼接路径
        std::string path=_web_root+uri;
        if(path.back()=='/')
        {
            path+="login.html";
        }
        DBG_LOG("path:%s",path.c_str());

        // Json::Value resp_json;
        std::string body;
        // 读取文件内容
        bool ret=File_Util::read(path,body);
        // 如果没有读取到文件，返回404网页
        if(ret==false)
        {
            File_Util::read(_404_,body);
            conn->set_status(websocketpp::http::status_code::not_found);
            conn->set_body(body);
            return;
        }
        conn->set_body(body);
        conn->set_status(websocketpp::http::status_code::ok);
    }

    // http响应函数
    void http_resp(wsserver_t::connection_ptr &conn,bool result,websocketpp::http::status_code::value code,const std::string &reason)
    {
        Json::Value resp_json;
        resp_json["result"]=result;
        resp_json["reason"]=reason;
        std::string resp_body;
        Json_Util::serialize(resp_json,resp_body);
        DBG_LOG("reason:%s",resp_body.c_str());
        conn->set_status(code);
        conn->set_body(resp_body);
        conn->append_header("Content-Type","application/json");
        return;
    }

    // 注册
    void registe(wsserver_t::connection_ptr &conn)
    {
        std::string req_body=conn->get_request_body();
        Json::Value register_info;
        bool ret = Json_Util::unserialize(req_body, register_info);
        if (ret == false)
        {
            ERR_LOG("unserialize failed!");
            return http_resp(conn, false, websocketpp::http::status_code::bad_request, "请求正文格式出错");
        }

        // 进程用户注册信息确认
        ret=_ut.insert(register_info);
        if(ret==false)
        {
            ERR_LOG("insert database failed!");
            return http_resp(conn, false, websocketpp::http::status_code::bad_request, "信息已被占用");
        }

        return http_resp(conn,true,websocketpp::http::status_code::ok,"注册成功");
    }

    // 登录
    void login(wsserver_t::connection_ptr &conn)
    {
        // 获取请求报头
        std::string req_body=conn->get_request_body();
        Json::Value login_info;
        // 将报文反序列化
        bool ret=Json_Util::unserialize(req_body,login_info);
        if(ret==false)
        {
            ERR_LOG("unserialize failed!");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"请求正文格式出错");
        }

        // 进行用户登录信息确认
        ret=_ut.login(login_info);
        if(ret==false)
        {
            ERR_LOG("user info mistake!");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"用户信息有误");
        }

        // 创建用户会话
        uint16_t uid=login_info["id"].asUInt64();
        session_ptr ssp=_sm.create_session(uid,LOGIN);
        if(ssp.get()==nullptr)
        {
            ERR_LOG("create session failed!");
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"创建会话失败");
        }
        _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);

        // 通过cookie返回会话id给用户
        std::string cookie_ssid="SSID="+std::to_string(ssp->ssid());
        cookie_ssid+="; Path=/; HttpOnly";
        conn->append_header("Set-Cookie",cookie_ssid);
        std::string hresp=conn->get_response_header("Set-Cookie");

        return http_resp(conn,true,websocketpp::http::status_code::ok,"登陆成功");
    }

    // 获取cookie信息
    bool get_cookie(const std::string &cookie_str,const std::string &key,std::string &val)
    {
        std::string sep=";";
        std::vector<std::string> cookie_arr;
        String_Util::split(cookie_str,sep,cookie_arr);
        // DBG_LOG("cookie:%s",cookie_arr[0].c_str());
        for(auto str:cookie_arr)
        {
            std::vector<std::string> tmp_arr;
            String_Util::split(str,"=",tmp_arr);
            // DBG_LOG("cookie:%s",tmp_arr[0].c_str());
            // DBG_LOG("cookie:%s",tmp_arr[1].c_str());
            if(tmp_arr[0]==key)
            {
                val=tmp_arr[1];
                return true;
            }
        }
        return false;
    }

    // 获取用户信息
    void info(wsserver_t::connection_ptr &conn)
    {
        // 从报头中获取cookie信息
        std::string cookie_str=conn->get_request_header("Cookie");
        // DBG_LOG("cookie:%s",cookie_str.c_str());
        if(cookie_str.empty())
        {
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到cookie信息,请重新登录");
        }

        // 从cookie中获取ssid
        std::string ssid_str;
        bool ret=get_cookie(cookie_str,"SSID",ssid_str);
        if(ret==false)
        {
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到ssid信息,请重新登录");
        }

        // 通过ssid查找session是否存在
        session_ptr ssp=_sm.get_session_by_ssid(std::stol(ssid_str));
        if(ssp.get()==nullptr)
        {
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"登录过期,请重新登录");
        }

        // 通过session获取用户uid，进而获取需要的信息
        uint64_t uid=ssp->get_user();
        Json::Value user_info;
        DBG_LOG("uid:%d",uid);
        ret=_ut.select_by_id(uid,user_info);
        if(ret==false)
        {
            return http_resp(conn,false,websocketpp::http::status_code::bad_request,"找不到用户信息,请重新登录");
        }

        // 将信息序列化发送
        std::string body;
        Json_Util::serialize(user_info,body);
        conn->set_body(body);
        conn->append_header("Content-Type","application/json");
        conn->set_status(websocketpp::http::status_code::ok);

        // 刷新session的时间
        _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);
    }

    // http请求的回调函数
    void http_callback(websocketpp::connection_hdl hdl)
    {
        // 获取connection_hdl 对应连接的connection_ptr
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        // 获取http请求对象
        websocketpp::http::parser::request req=conn->get_request();
        // 获取请求方法和uri
        std::string method=req.get_method();
        std::string uri=req.get_uri();
        DBG_LOG("method:%s,uri:%s",method.c_str(),uri.c_str());

        // 请求注册注册时
        if(method=="POST"&&uri=="/reg")
        {
            return registe(conn);
        }
        // 请求登陆信息时
        else if(method=="POST"&&uri=="/login")
        {
            return login(conn);
        }
        // 请求用户信息时
        else if(method=="GET"&&uri=="/info")
        {
            return info(conn);
        }
        // 请求静态网页时
        else
        {
            return file_handler(conn);
        }
    }

    // websocket的响应函数
    void ws_resp(wsserver_t::connection_ptr conn,Json::Value &resp)
    {
        std::string body;
        Json_Util::serialize(resp,body);
        conn->send(body);
    }

    // 通过cookie获得用户会话
    session_ptr get_session_by_cookie(wsserver_t::connection_ptr conn)
    {
        Json::Value err_resp;

        // 获得连接报头cookie内容
        std::string cookie_str=conn->get_request_header("Cookie");
        if(cookie_str.empty())
        {
            err_resp["optype"]="hall_ready";
            err_resp["reason"]="找不到cookie信息,请重新登录";
            err_resp["result"]=false;
            ws_resp(conn,err_resp);
            return session_ptr();
        }

        // 从cookie中获取ssid
        std::string ssid_str;
        bool ret=get_cookie(cookie_str,"SSID",ssid_str);
        if(ret==false)
        {
            err_resp["optype"]="hall_ready";
            err_resp["reason"]="找不到ssid信息,请重新登录";
            err_resp["result"]=false;
            ws_resp(conn,err_resp);
            return session_ptr();
        }

        // 通过ssid找到session
        session_ptr ssp=_sm.get_session_by_ssid(std::stol(ssid_str));
        if(ssp.get()==nullptr)
        {
            err_resp["optype"]="hall_ready";
            err_resp["reason"]="登录过期,请重新登录";
            err_resp["result"]=false;
            ws_resp(conn,err_resp);
            return session_ptr();
        }

        return ssp;
    }

    // 建立websocket大厅连接的处理函数
    void wsopen_game_hall(wsserver_t::connection_ptr conn)
    {
        Json::Value resp_json;

        // 通过客户端返回的cookie获得session
        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            return;
        }

        // 判断用户session是否在大厅或者游戏房间里
        if(_om.in_game_hall(ssp->get_user())||_om.in_game_room(ssp->get_user()))
        {
            resp_json["optype"]="hall_ready";
            resp_json["reason"]="用户重复登录";
            resp_json["result"]=false;
            return ws_resp(conn,resp_json);
        }

        // 加入用户session到游戏大厅
        _om.enter_game_hall(ssp->get_user(),conn);
        resp_json["optype"]="hall_ready";
        resp_json["reason"]="连接大厅成功";
        resp_json["result"]=true;
        ws_resp(conn,resp_json);

        // 设置用户session时间为永久
        _sm.set_session_expire_time(ssp->ssid(),SESSION_FOREVER);
    }

    // 建立websocket房间连接的处理函数
    void wsopen_game_room(wsserver_t::connection_ptr conn)
    {
        DBG_LOG("wsopen_game_room begin");
        Json::Value resp_json;

        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            return;
        }

        if(_om.in_game_hall(ssp->get_user())||_om.in_game_room(ssp->get_user()))
        {
            resp_json["optype"]="room_ready";
            resp_json["reason"]="用户重复登录";
            resp_json["result"]=false;
            return ws_resp(conn,resp_json);
        }

        room_ptr rp=_rm.get_room_by_uid(ssp->get_user());
        if(rp.get()==nullptr)
        {
            resp_json["optype"]="room_ready";
            resp_json["reason"]="没有找到玩家房间信息";
            resp_json["result"]=false;
            return ws_resp(conn,resp_json);            
        }
        
        DBG_LOG("wsopen_game_room mid1");
        if(rp->statu()==GAME_RECONNECT)
        {
            rp->set_statu(GAME_START);
            rp->cancel_tp();
            DBG_LOG("wsopen_game_room mid2");
        }

        _om.enter_game_room(ssp->get_user(),conn);
        _sm.set_session_expire_time(ssp->ssid(),SESSION_FOREVER);

        // 将房间信息设置为响应信息并返回
        resp_json["optype"]="room_ready";
        resp_json["result"]=true;
        resp_json["room_id"]=(Json::UInt64)rp->id();
        resp_json["uid"]=(Json::UInt64)ssp->get_user();
        resp_json["white_uid"]=(Json::UInt64)rp->get_white_user();
        resp_json["black_uid"]=(Json::UInt64)rp->get_black_user();

        DBG_LOG("wsopen_game_room tail");
        return ws_resp(conn,resp_json);
    }

    // websocket链接的建立请求
    void wsopen_callback(websocketpp::connection_hdl hdl)
    {
        // 获取websocket连接
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall")
        {
            return wsopen_game_hall(conn);
        }
        else if(uri=="/room")
        {
            return wsopen_game_room(conn);
        }
    }

    // 断开websocket大厅连接的处理函数
    void wsclose_game_hall(wsserver_t::connection_ptr conn)
    {
        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            return;
        }

        // 在在线用户管理类中移除用户，并将用户session设为定时关闭
        _om.exit_game_hall(ssp->get_user());
        _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);
    }

    // 断开websocket房间连接的处理函数
    void wsclose_game_room(wsserver_t::connection_ptr conn)
    {
        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            return;
        }

        // 在在线用户管理类中移除用户，并将用户session设为定时关闭，之后移除房间中的用户
        _om.exit_game_room(ssp->get_user());
        _sm.set_session_expire_time(ssp->ssid(),SESSION_TIMEOUT);
        _rm.remove_room_user(ssp->get_user());
    }

    // websocket链接的断开请求
    void wsclose_callback(websocketpp::connection_hdl hdl)
    {
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall")
        {
            return wsclose_game_hall(conn);
        }
        else if(uri=="/room")
        {
            return wsclose_game_room(conn);
        }
    }

    // 收到websocket大厅信息的处理函数
    void wsmsg_game_hall(wsserver_t::connection_ptr conn,wsserver_t::message_ptr msg)
    {
        Json::Value resp_json;

        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            return;
        }

        // 获取收到的信息内容
        std::string req_body=msg->get_payload();
        Json::Value req_json;
        bool ret=Json_Util::unserialize(req_body,req_json);
        if(ret==false)
        {
            resp_json["result"]=false;
            resp_json["reason"]="请求信息解析失败";
            return ws_resp(conn,resp_json);
        }

        // 收到开始匹配的信息
        if(req_json["optype"].asString()=="match_start")
        {
            // 将收到的用户id加入匹配队列，返回匹配中信息
            _mm.add(ssp->get_user());
            resp_json["optype"]="match_start";
            resp_json["result"]=true;
            resp_json["reason"]="匹配中";
            return ws_resp(conn,resp_json);           
        }
        // 收到停止匹配的信息
        else if(req_json["optype"].asString()=="match_stop")
        {
            // 将收到的用户id退出匹配队列，返回退出匹配信息
            _mm.del(ssp->get_user());
            resp_json["optype"]="match_stop";
            resp_json["result"]=true;
            resp_json["reason"]="停止匹配";
            return ws_resp(conn,resp_json);               
        }
        // 其他类型信息报错
        resp_json["optype"]="unknow";
        resp_json["result"]=false;
        resp_json["reason"]="请求类型未知";
        return ws_resp(conn,resp_json); 
    }

    // 收到websocket房间信息的处理函数
    void wsmsg_game_room(wsserver_t::connection_ptr conn,wsserver_t::message_ptr msg)
    {
        DBG_LOG("wsmsg_game_room begin");
        Json::Value resp_json;

        session_ptr ssp=get_session_by_cookie(conn);
        if(ssp.get()==nullptr)
        {
            ERR_LOG("not find session");
            return;
        }

        // 通过ssid查找用户所在房间
        room_ptr rp=_rm.get_room_by_uid(ssp->get_user());
        if(rp.get()==nullptr)
        {
            resp_json["optype"]="unknow";
            resp_json["reason"]="没有找到玩家房间信息";
            resp_json["result"]=false;
            ERR_LOG("not find room");
            return ws_resp(conn,resp_json);      
        }

        Json::Value req_json;
        std::string req_body=msg->get_payload();
        bool ret=Json_Util::unserialize(req_body,req_json);
        if(ret==false)
        {
            resp_json["optype"]="unknow";
            resp_json["reason"]="请求解析失败";
            resp_json["result"]=false;
            ERR_LOG("unserialize failed");
            return ws_resp(conn,resp_json);   
        }
        INF_LOG("receive room request");

        DBG_LOG("wsmsg_game_room tail");

        // 将正确的信息传入房间的处理响应函数
        return rp->handle_request(req_json);
    }

    // websocket链接的发送信息请求
    void wsmessage_callback(websocketpp::connection_hdl hdl,wsserver_t::message_ptr msg)
    {
        DBG_LOG("wsmessage_callback begin");
        wsserver_t::connection_ptr conn=_wssrv.get_con_from_hdl(hdl);
        websocketpp::http::parser::request req=conn->get_request();
        std::string uri=req.get_uri();
        if(uri=="/hall")
        {
            return wsmsg_game_hall(conn,msg);
        }
        else if(uri=="/room")
        {
            return wsmsg_game_room(conn,msg);
        }
        DBG_LOG("wsmessage_callback tail");
    }

private:
    wsserver_t _wssrv;              //服务器
    std::string _web_root;          //根目录
    User_Table _ut;                 //用户管理类
    Session_Manager _sm;            //用户会话管理类
    Online_Manager _om;             //在线用户管理类
    Room_Manager _rm;               //房间管理类
    Matcher _mm;                    //匹配队列管理类
};


#endif
